#
# fusion.http.Profile
#
# Copyright 2007 Helsinki Institute for Information Technology
# and the authors.
#
# Authors: Ken Rimey <rimey@hiit.fi>
#

# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
# (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

'''
Handle URLs of the form "/profiles?/([0-9a-f]{40})".
'''

import web
import simplejson

from fusion.http.util import (
    withdb, format_xml, read_form,
    authorized_referer, unauthorized)
from fusion.http.server import ADMIN
from fusion.db.db import hex_encode_for_path
from fusion.db.exceptions import *

profile_html = '''<html>
<head>
<script type="text/javascript" src="/static/js/profile.js" />
</head>
<body></body>
</html>
'''

class Profile(object):

    @withdb
    def GET(self, db, collection=None):
        if collection is None:
            my_profile = db.get_local_info('default_profile')
            if my_profile:
                return web.found(my_profile + web.ctx.query)
            else:
                return web.notfound()

        input = web.input(edit='', friend='')

        try:
            type = db.get(collection, 'info/type',
                          autoimport = True,
                          block = True)
            if type != 'tag:p2p-fusion,2007:profile':
                return web.notfound()

            name = db.get_text(collection, 'info/title') or u''

            json_data = db.get(collection, 'profile', block=True)
            try:
                data = simplejson.loads(json_data)
            except ValueError:
                data = {}
        except NoSuchCollection:
            return web.notfound()

        web.header('Content-Type', 'application/xhtml+xml; charset=UTF-8')

        print '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"'
        print '    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'
        print '<html xmlns="http://www.w3.org/1999/xhtml">'
        print '<head>%s</head>' % format_xml('title', name)
        print '<body>'

        print '<em>' + format_xml('a', 'admin', href='/') + '</em>'
        print '/'
        profiles_url = ADMIN + '/profiles/'
        print '<em>' + format_xml('a', 'profiles', href=profiles_url) + '</em>'
        print '/'
        print format_xml('a', name, href='/profile/' + collection)

        if input.edit:
            url = '/collection/' + collection + '?next=/profile/' + collection
            output_form(url, name, data)
        elif input.friend:
            print format_xml('h1', name, align='center')
            output_friend_confirmation(db, collection, input.friend)
        else:
            output_profile(collection, name, data)
            output_friends(db, collection)

            if db.has_rights(collection, 'put'):
                print format_xml('a', 'Edit this profile', href='?edit=true')

            my_profile = db.get_local_info('default_profile')
            if my_profile and collection != my_profile:
                text = 'List this person as a friend in your profile'
                url = '/profile/' + my_profile + '?friend=' + collection
                print format_xml('a', text, href=url)

        print '</body>'
        print '</html>'

    @withdb
    def POST(self, db, collection=None):
        if collection is None:
            collection = db.get_local_info('default_profile')
            if not collection:
                return web.notfound()

        if not authorized_referer(collection):
            return unauthorized()

        form = read_form()
        if 'add_friend' in form:
            name = form.getfirst('name')
            profile = form.getfirst('profile')
            info = dict(name=name, profile=profile)
            value = simplejson.dumps(info)

            identity = db.get_owner(profile)
            fingerprint = identity.get_fingerprint()
            key = 'member_info/' + hex_encode_for_path(fingerprint)

            db.add_member(collection, 'friend', identity)
            db.put(collection, key, value)
            return web.seeother('')

def output_friend_confirmation(db, collection, friend):
    name = db.get_text(friend, 'info/title')
    name = name or 'UNKNOWN'
    url = '/profile/' + friend

    print '<p>'
    print 'Please confirm that you want to list'
    print format_xml('a', name, href=url)
    print 'as a friend in this profile.'
    print '</p><p>'
    print 'You must also enter a nickname to use for this person.'
    print 'You can always change the nickname by adding the person again.'
    print '</p>'

    print '<form action="" method="post">'
    print 'Nickname: '
    print format_xml('input', type='text', name='name', value=name)
    print format_xml('input', type='hidden', name='profile', value=friend)
    print format_xml('input', type='submit',
                     name='add_friend', value='Confirm')
    print '</form>'

def output_profile(collection, name, data):
    data = dict(data)
    picture = pop_value(data, 'picture', 'file')

    print format_xml('h1', name, align='center')

    if picture:
        url = '/collection/%s/file/%s' % (collection, picture)
        print format_xml('img', src=url, alt='Picture', align='right')

    if data:
        print '<dl>'
        for k, v in sorted(data.iteritems(), key=get_sort_key):
            if isinstance(v, dict):
                label = v.get('_label', k.capitalize().rstrip('0123456789'))
                if 'text' in v:
                    v = v['text']
                    print format_xml('dt', label + ':')
                    if v.startswith('http:') or v.startswith('/'):
                        # XXX It looks like there is no point to the "url" type.
                        print '<dd>%s</dd>' % format_xml('a', v, href=v)
                    else:
                        print format_xml('dd', v)
                elif 'url' in v:
                    v = v['url']
                    print format_xml('dt', label + ':')
                    print '<dd>%s</dd>' % format_xml('a', v, href=v)
        print '</dl>'

def output_friends(db, collection):
    view = db.get_view(collection, 'member_info')

    friends = []
    for identity in db.list_members(collection, 'friend'):
        key = hex_encode_for_path(identity.get_fingerprint())
        json = view.get(key)
        if json:
            info = simplejson.loads(json)
            name = info.get('name')
            profile = info.get('profile')
            friends.append((name, profile))
        else:
            friends.append((None, None))

    if friends:
        links = []
        for name, profile in sorted(friends):
            name = name or 'UNKNOWN'
            if not profile:
                link = format_xml('a', name)
            else:
                url = '/profile/' + profile
                link = format_xml('a', name, href=url)

            links.append(link)

        print '<dl>'
        print '<dt>Friends:</dt>'
        print '<dd>'
        print ', '.join(links)
        print '</dd>'
        print '</dl>'

def output_form(url, name=u'', data={}):
    data = dict(data)

    print '<form action="%s"' % url # URL must not require escaping.
    print '      enctype="multipart/form-data" method="post">'
    print '<input type="hidden" name="put:info/type"'
    print '       value="tag:p2p-fusion,2007:profile" />'
    print format_xml('input', type='hidden',
                     name='put:web/index.html', value=profile_html)
    print '<p><table>'

    print '<tr><td>Name (<em>required</em>):</td><td>'
    print format_xml('input', type='text', name='put:info/title', value=name)
    print '</td></tr>'

    pop_and_output_file_field(data, 'file', 'picture', 'Picture')

    pop_and_output_text_field(data, 'text', 'country', 'Country', 10)
    pop_and_output_text_field(data, 'text', 'region', 'State/Province', 12)
    pop_and_output_text_field(data, 'text', 'city', 'City', 14)

    pop_and_output_radio_buttons(data, 'text', 'gender', 'Gender', 20,
                                 choices=('Unspecified', 'Male', 'Female'))

    pop_and_output_text_field(data, 'text', 'born', 'Year of Birth', 30,
                              size='4', maxlength='4')

    pop_and_output_textarea(data, 'text', 'about', 'About Me', 40,
                           rows='6', cols='40')

    pop_and_output_text_field(data, 'url', 'website', 'Website', 50,
                              size='42')

    pop_and_output_text_field(data, 'text', 'phone1', 'Phone', 60)
    pop_and_output_text_field(data, 'text', 'phone2', 'Phone', 60)
    pop_and_output_text_field(data, 'text', 'email1', 'Email', 70)
    pop_and_output_text_field(data, 'text', 'email2', 'Email', 70)
    pop_and_output_text_field(data, 'text', 'im1', 'Instant Messaging', 70)
    pop_and_output_text_field(data, 'text', 'im2', 'Instant Messaging', 70)

    output_other_text_fields(data)
    output_extra_text_fields(data)

    print '</table></p>'

    print '<p><input type="submit" value="Submit" /></p>'
    print '</form>'

def pop_and_output_text_field(data, type, key, label, position=None, **attrs):
    value = pop_value(data, key, type)
    field = format_xml('input', type='text', value=value,
                       name = 'put:profile/-/%s/%s' % (key, type),
                       **attrs)
    output_field(field, key, label, position)

def pop_and_output_textarea(data, type, key, label, position=None, **attrs):
    value = pop_value(data, key, type)
    field = format_xml('textarea', value,
                       name = 'put:profile/-/%s/%s' % (key, type),
                       **attrs)
    output_field(field, key, label, position)

def pop_and_output_radio_buttons(data, type, key, label, position=None, choices=()):
    value = pop_value(data, key, type)

    buffer = []
    for choice in choices:
        attrs = {'checked': 'checked'} if choice == value else {}
        buffer.append(format_xml('input', type='radio', value=choice,
                                 name = 'put:profile/-/%s/%s' % (key, type),
                                 **attrs))
        buffer.append(choice)

    output_field('\n'.join(buffer), key, label, position)

def pop_and_output_file_field(data, type, key, label, position=None, **attrs):
    filename = pop_value(data, key, type)

    buffer = []
    if filename:
        buffer.append(format_xml('input', type='hidden', value=filename,
                                 name = 'put:profile/-/%s/%s_default' % (key, type)))
    buffer.append(format_xml('input', type='file',
                             name = 'put_filename:profile/-/%s/%s' % (key, type),
                             **attrs))
    output_field('\n'.join(buffer), key, label, position)

def pop_value(data, key, type):
    v = data.pop(key, None)
    if isinstance(v, dict):
        return v.get(type, '')
    else:
        return ''

def output_other_text_fields(data):
    for key, v in sorted(data.iteritems(), key=get_sort_key):
        if isinstance(v, dict):
            for type in ('text', 'url'):
                if type in v:
                    break
            else:
                continue

            value = v[type]
            if value:
                label = v.get('_label', 'Other')
                position = v.get('_position')

                field = format_xml('input', type='text', value=value,
                                   name = 'put:profile/-/%s/%s' % (key, type))
                output_field(field, key, label, position)

def output_extra_text_fields(data):
    i = 0
    count = 2
    while count > 0:
        i += 1
        key = 'extra%d' % i
        if key not in data:
            count -= 1
            print '<tr>'
            print '<td>'
            print format_xml('input', type='text',
                             name = 'put:profile/-/%s/_label' % key) + ':'
            print '</td>'
            print '<td>'
            print format_xml('input', type='text',
                             name = 'put:profile/-/%s/text' % key)
            print '</td>'
            print '</tr>'

def output_field(field, key, label, position=None):
    print '<tr>'
    print format_xml('td', label + ':')
    print '<td>'
    print field
    print format_xml('input', type='hidden', value=label,
                     name = 'put:profile/-/%s/_label' % key)
    if position:
        print format_xml('input', type='hidden', value=str(position),
                         name = 'put:profile/-/%s/_position' % key)
    print '</td>'
    print '</tr>'

def get_sort_key((k, v)):
    try:
        position = int(v['_position'])
    except StandardError:
        position = 10**9

    label = v.get('_label')

    return position, label, k
